/*
 * PlayerHolder.cpp
 *
 *  Created on: 2018年7月25日
 *      Author: terry
 */

#include "PlayerHolder.h"
#include "FfmpegUtil.h"
#include "AVFrameUtil.h"
#include "TStringUtil.h"
#include "MediaProp.h"
#include "CLog.h"


/// 单位为微秒
int PlayerHolder::FACE_TRACK_DURATION = 1000 * 400;


PlayerHolder::PlayerHolder():
	m_eventCb(),
	m_eventUser(),
	m_msgWnd(),
	m_msg(),
	m_pixelFormat(),
	m_decodeCb(),
	m_decodeUser(),
	m_frameCount(),
	m_decodeFreq()
{
	m_srcFrame.reset(av_frame_alloc(), ffmpeg::AVFrameDeleter());
	m_outFrame.reset(av_frame_alloc(), ffmpeg::AVFrameDeleter());

}

PlayerHolder::~PlayerHolder()
{
	close();
}

void PlayerHolder::close()
{
	if (m_player)
	{
		m_player->close();
	}

	m_player.reset();

	m_input.reset();

	m_scaleContext.reset();
}

bool PlayerHolder::isOpen()
{
	return m_player.get() != NULL;
}

bool PlayerHolder::openStream(const MDecode_Format* fmt)
{
	m_input.reset(av::LitePlayerFactory::createInputPlayer(NULL), av::LitePlayerFactory::destroy);
	m_player = m_input;

	m_player->setEventSink(this);

	m_player->setProp(MEDIA_PLAYER_PROP_MAX_DURATION, "4000");

	if (m_paramSet.size() > 0)
	{
		for (size_t i = 0; i < m_paramSet.size(); i++)
		{
			std::string key;
			std::string value;
			m_paramSet.getAt(i, key, value);
			m_player->setProp(key.c_str(), value.c_str());
		}
	}


	MFormat format = MFormat();
	format.codec = fmt->codec;
	format.channels = fmt->channels;
	format.sampleRate = fmt->samplerate;
	format.audioCodec = fmt->audioCodec;
	format.vProp = fmt->prop;
	format.vPropSize = fmt->propSize;
	format.configSize = fmt->configSize;
	format.config = fmt->config;

	int ret = m_input->open(format);
	return (ret == 0);
}

bool PlayerHolder::openFile(const char* url, const char* params)
{
	m_input.reset();
	m_player.reset(av::LitePlayerFactory::create(NULL), av::LitePlayerFactory::destroy);

	m_player->setEventSink(this);

	int ret = m_player->open(url, params);
	return (ret == 0);
}

bool PlayerHolder::inputData(int mediaType, const uint8_t* buffer, int length, int64_t pts, int flags)
{
	if (!m_input)
	{
		return false;
	}

	//CLog::debug("inputData. %lld\n", pts);

	//if (mediaType == MDECODE_MEDIA_VIDEO)
	//{
	//	inputVideo(buffer, length, pts, flags);
	//	return true;
	//}

	MPacket pkt = MPacket();
	pkt.type = mediaType;
	pkt.data = const_cast<uint8_t*>(buffer);
	pkt.size = length;
	pkt.pts = pts;
	pkt.flags = flags;

	if (mediaType == MDECODE_MEDIA_SUBTITLE)
	{
		pkt.duration = FACE_TRACK_DURATION;
		pkt.flags += av::MEDIA_SUBTITLE_FACE;
	}

	return m_input->write(pkt);
}

void PlayerHolder::resetBuffer()
{
	if (!m_input)
	{
		return ;
	}

	m_input->flush();
}

bool PlayerHolder::setVideoWnd(HWND hwnd)
{
	if (!m_player)
	{
		return false;
	}

	m_player->setVideoWnd(hwnd);
	return true;
}

bool PlayerHolder::getPictureSize(int& width, int& height)
{
	if (!m_player)
	{
		return false;
	}

	av::MediaFormat format;
	m_player->getFormat(format);
	width = format.m_width;
	height = format.m_height;

	return true;
}

bool PlayerHolder::snap(const char* filename)
{
	if (!m_player)
	{
		return false;
	}

	int ret = m_player->snap(filename);
	return ret == 0;
}

void PlayerHolder::refresh()
{
	//todo
}

bool PlayerHolder::play()
{
	if (!m_player)
	{
		return false;
	}

	int ret = m_player->play();
	return ret == 0;
}

bool PlayerHolder::pause(bool toPause)
{
	if (!m_player)
	{
		return false;
	}

	int ret = toPause ? m_player->pause() : m_player->play();
	return ret == 0;
}

void PlayerHolder::stop()
{
	if (!m_player)
	{
		return;
	}

	m_player->stop();
}

bool PlayerHolder::setDecodeCallback(MDecode_PixelFormat pixelFormat, MediaDecodeCallback cb, void* pUser)
{
	if (!m_player)
	{
		return false;
	}

	{
		comn::AutoCritSec lock(m_cs);

		m_pixelFormat = pixelFormat;
		m_decodeCb = cb;
		m_decodeUser = pUser;
	}

	m_scaleContext.reset((AVPixelFormat)m_pixelFormat);

	if (m_decodeCb)
	{
		m_player->setFrameSink(this);
	}

	return true;
}

bool PlayerHolder::setDrawCallback(MediaDrawCallback cb, void* pUser)
{
	return false;
}

double PlayerHolder::getPos()
{
	if (!m_player)
	{
		return 0.0;
	}

	int duration = m_player->getDuration();
	if (duration <= 0)
	{
		return 0.0;
	}

	double pts = (double)m_player->getTime();

	return pts /1000 / duration ;
}

bool PlayerHolder::setPos(double pos)
{
	if (!m_player)
	{
		return false;
	}

	int duration = m_player->getDuration();
	if (duration <= 0)
	{
		return false;
	}

	double pts = pos * duration * 1000;
	int ret = m_player->seek((int64_t)pts);
	return (ret == 0);
}

int PlayerHolder::getDuration()
{
	if (!m_player)
	{
		return false;
	}

	return m_player->getDuration();
}

bool PlayerHolder::setScale(double scale)
{
	if (!m_player)
	{
		return false;
	}

	int ret = m_player->setScale((float)scale);
	return ret == 0;
}

double PlayerHolder::getScale()
{
	if (!m_player)
	{
		return 1.0;
	}

	return m_player->getScale();
}

bool PlayerHolder::setFileEndMsg(HWND hwnd, uint32_t msg)
{
	comn::AutoCritSec lock(m_cs);
	m_msgWnd = hwnd;
	m_msg = msg;

	return true;
}

bool PlayerHolder::openSound()
{
	if (!m_player)
	{
		return false;
	}

	m_player->setMute(false);

	return true;
}

void PlayerHolder::closeSound()
{
	if (!m_player)
	{
		return ;
	}

	m_player->setMute(true);

	return;
}

bool PlayerHolder::isSoundOpen()
{
	if (!m_player)
	{
		return false;
	}

	return m_player->isMute();
}

float PlayerHolder::getVolume()
{
	if (!m_player)
	{
		return 0.0;
	}

	return m_player->getVolume();
}

bool PlayerHolder::setVolume(float vol)
{
	if (!m_player)
	{
		return false;
	}

	int ret = m_player->setVolume(vol);
	return (ret == 0);
}

int64_t PlayerHolder::getTime()
{
	if (!m_player)
	{
		return false;
	}

	return m_player->getTime();
}

bool PlayerHolder::seek(int64_t pts)
{
	if (!m_player)
	{
		return false;
	}

	int ret = m_player->seek(pts);

	return ret == 0;
}

bool PlayerHolder::setRatio(int cx, int cy)
{
	if (!m_player)
	{
		return false;
	}

	m_player->setRatio(cx, cy);
	return true;
}

bool PlayerHolder::setEventCallback(DecoderEventCallback cb, void* pUser)
{
	comn::AutoCritSec lock(m_cs);
	m_eventCb = cb;
	m_eventUser = pUser;

	return true;
}

void PlayerHolder::fireEvent(int event, const char* value, int size)
{
	if (!m_eventCb)
	{
		return ;
	}

	(*m_eventCb)(this, event, value, size, m_eventUser);
}

void PlayerHolder::fireWndMsg(int event)
{
#ifdef WIN32
	if (m_msgWnd)
	{
		PostMessage(m_msgWnd, m_msg, (WPARAM)this, event);
	}
#endif //
}

void PlayerHolder::onMediaEvent(void* object, int type, const char* value, int size)
{
	av::LitePlayer* player = (av::LitePlayer*)object;
	fireEvent(type, value, size);

	if (type == av::MEDIA_EVENT_END)
	{
		fireWndMsg(type);
	}
}

void PlayerHolder::onMediaFrameFormat(const MFormat& format)
{
	/// pass
}

void PlayerHolder::onMediaFrame(MFrame& frame)
{
	if (!m_decodeCb)
	{
		return;
	}

	if (frame.type != MTYPE_VIDEO)
	{
		return;
	}

	m_frameCount++;

	if (m_decodeFreq > 1)
	{
		if (m_frameCount % m_decodeFreq != 1)
		{
			return;
		}
	}

	MDecode_Frame vframe = MDecode_Frame();
	vframe.type = frame.type;
	vframe.fmt = frame.fmt;
	vframe.width = frame.width;
	vframe.height = frame.height;
	vframe.flags = frame.flags;
	vframe.pts = frame.pts;

	if ((frame.fmt == m_pixelFormat) || (m_pixelFormat == MDECODE_PIXEL_NONE))
	{
		for (int i = 0; i < MDECODE_MAX_PLANE; i ++)
		{
			vframe.data[i] = frame.data[i];
			vframe.linesize[i] = frame.linesize[i];
		}

		(*m_decodeCb)(this, &vframe, m_decodeUser);
	}
	else 
	{
		m_srcFrame->format = frame.fmt;
		m_srcFrame->width = frame.width;
		m_srcFrame->height= frame.height;
		m_srcFrame->pts = frame.pts;
		m_srcFrame->flags = frame.flags;

		for (int i = 0; i < MDECODE_MAX_PLANE; i ++)
		{
			m_srcFrame->data[i] = frame.data[i];
			m_srcFrame->linesize[i] = frame.linesize[i];
		}

		m_scaleContext.update(m_srcFrame.get(), frame.width, frame.height);

		if (m_outFrame->width != frame.width || m_outFrame->height != frame.height)
		{
			m_outFrame.reset(av_frame_alloc(), ffmpeg::AVFrameDeleter());

			m_outFrame->width = frame.width;
			m_outFrame->height = frame.height;
			m_outFrame->format = m_pixelFormat;

			av_frame_get_buffer(m_outFrame.get(), 1);
		}

		sws_scale(m_scaleContext.m_pContext, m_srcFrame->data, m_srcFrame->linesize, 0,
				m_outFrame->height, m_outFrame->data, m_outFrame->linesize);

		for (int i = 0; i < MDECODE_MAX_PLANE; i ++)
		{
			vframe.data[i] = m_outFrame->data[i];
			vframe.linesize[i] = m_outFrame->linesize[i];
		}

		//AVFrameUtil::saveAsBmp(m_outFrame.get(), "dump.bmp");

		(*m_decodeCb)(this, &vframe, m_decodeUser);
	}
}

void PlayerHolder::onMediaFrameEnd()
{
	/// pass
}

bool PlayerHolder::setVideoFilter(const char* filterString)
{
	if (!m_player)
	{
		return false;
	}

	m_player->setVideoFilter(filterString);
	return true;
}


bool PlayerHolder::startRecord(const char* filepath)
{
	if (!m_player)
	{
		return false;
	}

	m_player->startRecord(filepath);
	return true;
}

void PlayerHolder::stopRecord()
{
	if (!m_player)
	{
		return ;
	}

	m_player->stopRecord();
}

bool PlayerHolder::isRecording()
{
	if (!m_player)
	{
		return false;
	}

	return m_player->isRecording();
}

bool PlayerHolder::setDelayTime(int milliseconds)
{
	//if (!m_player)
	//{
	//	return false;
	//}

	if (!m_input)
	{
		return false;
	}

	//std::string value = comn::StringCast::toString(milliseconds);
	//return m_player->setProp(MEDIA_PLAYER_PROP_DELAY_TIME, value.c_str());

	m_input->setDelayTime(milliseconds);

	return true;
}

bool PlayerHolder::setAudioFormat(int codec, int channels, int samplerate, const uint8_t* config, int configSize)
{
	if (!m_player)
	{
		return false;
	}

	std::string name = comn::StringCast::toString(codec);
	if (codec == av::MEDIA_CODEC_G711A)
	{
		name = "PCMA";
	}
	else if (codec == av::MEDIA_CODEC_G711U)
	{
		name = "PCMU";
	}

	std::string value = comn::StringUtil::format("%s/%d/%d",
		name.c_str(), samplerate, channels);
	return m_player->setProp(MEDIA_PLAYER_PROP_AUDIO_CHANGED, value.c_str());
}

bool PlayerHolder::setParam(const char* name, const char* value)
{
	if (!m_player)
	{
		m_paramSet.set(name, value);
		return false;
	}
	
	return m_player->setProp(name, value);
}

bool PlayerHolder::setDecodeCallbackFreq(int freq)
{
	comn::AutoCritSec lock(m_cs);
	m_decodeFreq = freq;
	return true;
}

void PlayerHolder::enableDrawGraph(int mode)
{
	if (!m_player)
	{
		return ;
	}

	m_player->enableDrawGraph(mode);
}

void PlayerHolder::setDrawGraph(const char* value, size_t size)
{
	if (!m_player)
	{
		return;
	}

	m_player->setDrawGraph(value, size);
}

const char* PlayerHolder::getDrawGraph()
{
	if (!m_player)
	{
		return "";
	}

	return m_player->getDrawGraph();
}

void PlayerHolder::removeDrawGraph(int mode)
{
	std::string str = comn::StringUtil::format("{\"remove\": %d}", mode);
	setDrawGraph(str.c_str(), str.size());
}

void PlayerHolder::setFullscreen(bool isFull)
{
	if (!m_player)
	{
		return ;
	}

	m_player->setFullscreen(isFull);
}

bool PlayerHolder::isFullscreen()
{
	if (!m_player)
	{
		return false;
	}

	return m_player->isFullscreen();
}